/*
xp_socket.c
offer tcp sokect use function realize
designed by lunnating

----------------------------
2021-10-21 09:35:59
create file and code first version
*/
#include "xp_sokect.h"
#include "cli_cmd.h"
#include "stdio.h"
#include "network/network.h"

#ifdef WITH_LWIP
#include "../../network\lwip\include\lwip\sockets.h"
#include "lwip/netif.h"
#include "lwip/inet.h"
#include "ethernetif.h"
extern struct netif fsl_netif0[];
#endif

/*
creat a client and connet to the server
param:
    ip: target ip
    port: target port
return:
    0 = success
    -1= fail
*/
int xp_socket_client(const char *ip,int port,SOCKET_client *client){
    #ifdef WITH_LWIP
    struct sockaddr_in target_server;
    int sock;
    
    
    client->server.port=port;
    strncpy(client->server.ip,ip,15);
    target_server.sin_family=AF_INET;
    target_server.sin_port=htons(port);
    target_server.sin_addr.s_addr=inet_addr(ip);

    sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock==-1){
        perr("create client to %s:%d fail ~ ,at socket()",ip,port);
        goto ERR;
    }
    client->sock=sock;

    struct timeval timeout;
    timeout.tv_sec  = 0xffffffff;
    timeout.tv_usec = 0;
    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
                   sizeof(timeout)) < 0) {
        perr("create client to %s:%d fail ~ ,at setsockopt()",ip,port);
        goto ERR;
    }

    if(connect(sock,(struct sockaddr *)(&target_server),sizeof(target_server))==-1){
        perr("create client to %s:%d fail ~, at connect().",ip,port);
        goto ERR;
    }
    #ifdef WITH_LWIP
    strncpy(client->self.ip,ip4addr_ntoa(&fsl_netif0[0].ip_addr),16);
    #endif
    println("TCP target %s:%d connected !",ip,port);
    return 0;

ERR:
    close(sock);
    return -1;
    #endif
}


/*
create a server and accept
param:
    port: server port
return:
    0 =success
    -1=fail
*/
int xp_socket_server(int port,SOCKET_server *server){
    #ifdef WITH_LWIP
    struct sockaddr_in self_server;
    int sock=0;

    strncpy(server->self.ip,ip4addr_ntoa((ip4_addr_t *)&fsl_netif0[0].ip_addr),16);
    server->self.port=port;

    self_server.sin_family=AF_INET;
    self_server.sin_port=htons(port);
    self_server.sin_addr.s_addr=htonl(INADDR_ANY);
    server->self.port=port;
    strncpy(server->self.ip,ip4addr_ntoa((ip4_addr_t *)&self_server.sin_addr.s_addr),15);

    sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock==-1){
        perr("create server on %d fail ~, at socket().",port);
        goto ERR;
    }
    server->sock=sock;

    int on=1;
    if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &on,
                   sizeof(on))) {
        perr("create client on %d fail ~ ,at setsockopt().",port);
        goto ERR;
    }
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
                   sizeof(on))) {
        perr("create client on %d fail ~ ,at setsockopt().",port);
        goto ERR;
    }

    if(bind(sock,(struct sockaddr *)&self_server,sizeof(self_server))==-1){
        perr("create server fail ~, at bind().");
        goto ERR;
    }

    if(listen(sock,MAXZ_link)==-1){
        perr("create server fail ~, at listen().");
        goto ERR;
    }
    return 0;

ERR:
    close(sock);
    return -1;
    #endif
}

/*
listen port
param:
    server: server obj
return:
    connect client obj
    NULL: accept fail
*/
ACCEPT_client *xp_socket_accept(SOCKET_server *server){
    #ifdef WITH_LWIP
    struct sockaddr_in cl={0};
    socklen_t len;
    ACCEPT_client *client;

    client=(ACCEPT_client *)malloc(sizeof(ACCEPT_client));
    memset(client,0,sizeof(ACCEPT_client));
    client->server=(void *)server;
    len=sizeof(cl);
    client->sock=accept(server->sock,&cl,&len);
    if(client->sock==-1){
        perr("socket %d on %d accept fail ~",server->sock,server->self.port);
        goto ERR;
    }

    client->client.port=cl.sin_port;
    strncpy(client->client.ip,ip4addr_ntoa((ip4_addr_t *)&cl.sin_addr.s_addr),15);
    return client;

ERR:
    free(client);
    return NULL;
    #endif
}


/*
free socket obj
param:
    obj: free obj
*/
int xp_socket_free(void *obj){
    free(obj);
}

/*
add a server accpeted client
    server: server obj
    client: accepted client
return:
    0 =success
*/
int xp_socket_server_addClient(SOCKET_server *server,ACCEPT_client *client){
    int flag=0;

    for(int i=0;i<MAXZ_link;i++){
        if(server->client[i]==NULL){
            server->client[i]=client;
            flag=1;
            break;
        }
    }
    if(flag==0){
        perr("server %d connected client over 5,can't add this client %s:%d.",server->self.port,client->client.ip,client->client.port);
        return -1;
    }
    return 0;
}

/*
delete a server client
param:
    server: server obj
    client: delete client
return:
    0 =success
    -1=fail
*/
int xp_socket_server_deleteClient(SOCKET_server *server,ACCEPT_client *client){
    int flag=0;
    for(int i=0;i<MAXZ_link;i++){
        if(server->client[i]->sock==client->sock){
            free(server->client[i]);
            server->client[i]=NULL;
            flag=1;
        }
    }
    if(flag==0){
        perr("NO need to delete client !");
        return -1;
    }
    return 0;
}


void xp_server_thread(void *arg){
    SERVER_create sc;
    ACCEPT_client *accept=NULL;
    char thread_name[50]={0};

    memcpy(&sc,arg,sizeof(sc));
    void xp_server_deal_client(void *arg);
    while(1){
        if(xp_socket_server(sc.port,sc.server)==-1){
            perr("create 10021 serever fail ~, will retry !");
            aos_msleep(1000);
            continue;
        }
        while(1){
            accept=xp_socket_accept(sc.server);
            if(accept==NULL){
                close(sc.server->sock);
                break;
            }
            accept->rec_deal=sc.server->rec_deal;
            if(xp_socket_server_addClient(sc.server,accept)==0){
                sprintf(thread_name,"server client %d",accept->sock);
                aos_task_new(thread_name,xp_server_deal_client,accept,3500);
            }
        }
        if(sc.server->close==1){
            aos_task_exit(0);
        }
    }
}


void xp_server_deal_client(void *arg){
    ACCEPT_client remote;
    char buf_data[BUFF_size]={0};

    if(arg==NULL){
        aos_task_exit(0);
    }
    memcpy(&remote,arg,sizeof(remote));
    remote.rec_data=buf_data;
    println("a client connected: %s:%d.",remote.client.ip,remote.client.port);
    while(1){
        memset(buf_data,0,sizeof(buf_data));
        remote.rec_len=xp_socket_recv(remote.sock,buf_data,sizeof(buf_data)-1);
        if(remote.rec_len==-1||remote.rec_len==0){
            close(remote.sock);
            println("remote client %s:%d closed !, rec_len= %d.",remote.client.ip,remote.client.port,remote.rec_len);
            xp_socket_server_deleteClient((SOCKET_server *)remote.server,&remote);
            aos_task_exit(0);
        }
        if(remote.rec_deal==NULL){
            println("rec data from %s:%d: %s",remote.client.ip,remote.client.port,buf_data);
            perr("no regist rec_deal() api.");
        }
        else{
            remote.rec_deal(&remote,buf_data,remote.rec_len);
        }
        aos_msleep(10);
    }
}

/*
create a server and regist deal function
param:
    port: server port
    deal_api: deal api
return: 
    the server obj
*/
SOCKET_server *xp_server_create(int port,void (*deal_api)(void *,char *,int)){
    SERVER_create ct={0};

    ct.port=port;
    ct.server=(SOCKET_server *)malloc(sizeof(SOCKET_server));
    memset(ct.server,0,sizeof(SOCKET_server));
    ct.server->rec_deal=deal_api;
    aos_task_new("server accept",xp_server_thread,&ct,3000);
    while(ct.server->sock==0){
        println("wait for server %d create !",port);
        aos_msleep(100);
    }
    return ct.server;
}


/*
tcp client recive thread
*/
void xp_client_recive_thread(void *arg){
    int rec_len=0;
    char buf[BUFF_size]={0};
    CLIENT_create cc={0};

    memcpy(&cc,arg,sizeof(cc));
    while (1)
    {   
        if(xp_socket_client(cc.ip,cc.port,cc.client)==-1){
            perr("create client fail,will retry. target: %s:%d.",cc.ip,cc.port);
            aos_msleep(1000);
            continue;
        }
        
        while(1){
            memset(buf,0,BUFF_size);
            rec_len=xp_socket_recv(cc.client->sock,buf,BUFF_size-1);
            if(rec_len==-1||rec_len==0){
                xp_socket_close(cc.client->sock);
                perr("TCP client to %s:%d disconnect,will retry. rec_len= %d.",cc.ip,cc.port,rec_len);
                aos_msleep(1000);
                break;
            }
            // else if(rec_len==0){
            //     aos_msleep(10);
            //     continue;
            // }
            if(cc.client->rec_deal==NULL){
                println("TCP client rec from %s:%d: %s.",cc.ip,cc.port,buf);
                perr("No regitst deal fun !");
            }
            else{
                cc.client->rec_deal(&cc.client->server,buf,rec_len);
            }
            aos_msleep(10);
        }
        aos_msleep(10);
    }
}


/*
create a client and regist recive deal function
param:
    ip: target ip
    port: target port
    deal_fun: deal function
return:
    client obj;
    NULL= create fail
*/
SOCKET_client *xp_client_create(char *ip,int port,void (*deal_fun)(SOCKET_msg *,char *,int)){
    CLIENT_create ct={0};

    strncpy(ct.ip,ip,15);
    ct.port=port;
    ct.client=(SOCKET_client *)malloc(sizeof(SOCKET_client));
    memset(ct.client,0,sizeof(*ct.client));
    ct.client->rec_deal=deal_fun;
    aos_task_new("TCP client",xp_client_recive_thread,&ct,3000);
    while(ct.client->sock==0){
        println("wait for client to %s:%d create !",ip,port);
        aos_msleep(100);
    }
    return ct.client;
}


/*********************************************read write***************************/
/*
socket write string
param:
    socket: write by the sockect
    str: write string
return:
    write lenght
    -1: write fail
*/
int xp_socket_writeString(int sock,const char *str){
    #ifdef WITH_LWIP
    return write(sock,str,strlen(str));
    #endif
}


/*
socket write data
param:
    socket: write by the socket
    data: write data
    len: write data length
return:
    write length
    -1: write fail
*/
int xp_socket_writeData(int sock,const void *data,int len){
    #ifdef WITH_LWIP
    return write(sock,data,len);
    #endif
}


/*
socket read data
param:
    sock: read from the socket
    data: read and saved  data
    len: read max length
return:
    read length
    -1: read fail
*/
int xp_socket_readData(int sock, void *data,int len){
    #ifdef WITH_LWIP
    return read(sock,data,len);
    #endif
}

/*
socket read data with end flag
param:
    sock: read from the socket
    data: read and saved data
    len: read max lenght
    flag: end flag
return:
    read lenght
    -1: read fail
*/
int xp_socket_recv(int sock,void *data,int len){
    #ifdef WITH_LWIP
    return recv(sock,data,len,MSG_WAITALL);
    #endif
}



/*
close socket
param:
    sokc: close the socket
return:
     0: success
    -1: close fail
*/
int xp_socket_close(int sock){
    #ifdef WITH_LWIP
    return close(sock);
    #endif
}


/********************log function api*****************************/
/*
log print api
param:
    server: log server
    str: print string
*/
void xp_socket_log_print(void *server,char *str){
    SOCKET_server *log_server=server; 
    if(log_server==NULL)return;
    for(int i=0;i<MAXZ_link;i++){
        if(log_server->client[i]!=NULL){
            xp_socket_writeString(log_server->client[i]->sock,str);
        }
    }
}


/*
cli cmd api
param:
    str: cmd string
*/
void xp_socket_cli_cmd(void *msg,char *str,int len){
    char type[20]={0},fun[20]={0},param[80]={0};
    sscanf(str,"xp %[^ \r\n] %[^ \r\n] %[^ \r\n]\r\n",type,fun,param);
    println("socket cmd: %s %s %s.",type,fun,param);
    xp_cli_cmd_set(type,fun,param);
}




/********************************tools********************************/
/*
addr string to int data
param:
    addr: addr string
return: data
*/
int xp_socket_addrTOint(const char *addr){
    char a[4];
    int ret;

    sscanf(addr,"%s.%s.%s.%s",a[0],a[1],a[2],a[3]);
    ret=a[0]<<24|a[1]<<16|a[2]<<8|a[3];
    return ret;
}


/*
addr data to string
param:
    addr: addr data
return:
    addr string
*/
char *xp_socket_intTOstring(int addr){
    static char as[16]={0};
    char a[4];

    a[0]=addr>>24;
    a[1]=addr>>16;
    a[2]=addr>>8;
    a[3]=addr;
    sprintf(as,"%d.%d.%d.%d",a[0],a[1],a[2],a[3]);
    return as;
}

//end of the file
